/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: NPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1998
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the NPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the NPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

//////////////////////////////////////////////////
//
// CPlugin class implementation
//
#ifdef XP_WIN
#include <stdio.h>
#include <windows.h>
#include <windowsx.h>
#endif

#ifdef XP_MAC
#include <TextEdit.h>
#endif

#ifdef XP_UNIX
#include <string.h>
#endif

#include "plugin.h"
#include "npfunctions.h"

static NPObject *sWindowObj;

static NPIdentifier tirIsCameraInitialized;
static NPIdentifier tirIsCameraRunning;
static NPIdentifier tirRecenter;
static NPIdentifier tirGetMatrix;

static NPIdentifier getMatrix0;
static NPIdentifier getMatrix1;
static NPIdentifier getMatrix2;
static NPIdentifier getMatrix3;
static NPIdentifier getMatrix4;
static NPIdentifier getMatrix5;
static NPIdentifier getMatrix6;
static NPIdentifier getMatrix7;
static NPIdentifier getMatrix8;
static NPIdentifier getMatrix9;
static NPIdentifier getMatrix10;
static NPIdentifier getMatrix11;
static NPIdentifier getMatrix12;
static NPIdentifier getMatrix13;
static NPIdentifier getMatrix14;
static NPIdentifier getMatrix15;
static float* matrixData = new float[16];

HINSTANCE tirDLL = LoadLibrary("TrackIR.dll");

typedef void (*tirSetupCameraProto)();
typedef void (*tirShutdownCameraProto)();
typedef bool (*tirIsCameraInitializedProto)();
typedef void (*tirStartCameraProto)();
typedef void (*tirStopCameraProto)();
typedef bool (*tirIsCameraRunningProto)();
typedef void (*tirRecenterProto)();
typedef void (*tirGetMatrixProto)(float[]);
tirSetupCameraProto tirSetupCamera_;
tirShutdownCameraProto tirShutdownCamera_;
tirIsCameraInitializedProto tirIsCameraInitialized_;
tirStartCameraProto tirStartCamera_;
tirStopCameraProto tirStopCamera_;
tirIsCameraRunningProto tirIsCameraRunning_;
tirRecenterProto tirRecenter_;
tirGetMatrixProto tirGetMatrix_;

// Helper class that can be used to map calls to the NPObject hooks
// into virtual methods on instances of classes that derive from this
// class.
class ScriptablePluginObjectBase : public NPObject
{
public:
  ScriptablePluginObjectBase(NPP npp)
    : mNpp(npp)
  {
  }

  virtual ~ScriptablePluginObjectBase()
  {
  }

  // Virtual NPObject hooks called through this base class. Override
  // as you see fit.
  virtual void Invalidate();
  virtual bool HasMethod(NPIdentifier name);
  virtual bool Invoke(NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result);
  virtual bool InvokeDefault(const NPVariant *args, uint32_t argCount, NPVariant *result);
  virtual bool HasProperty(NPIdentifier name);
  virtual bool GetProperty(NPIdentifier name, NPVariant *result);
  virtual bool SetProperty(NPIdentifier name, const NPVariant *value);
  virtual bool RemoveProperty(NPIdentifier name);
  virtual bool Enumerate(NPIdentifier **identifier, uint32_t *count);
  virtual bool Construct(const NPVariant *args, uint32_t argCount, NPVariant *result);

public:
  static void _Deallocate(NPObject *npobj);
  static void _Invalidate(NPObject *npobj);
  static bool _HasMethod(NPObject *npobj, NPIdentifier name);
  static bool _Invoke(NPObject *npobj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result);
  static bool _InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result);
  static bool _HasProperty(NPObject * npobj, NPIdentifier name);
  static bool _GetProperty(NPObject *npobj, NPIdentifier name, NPVariant *result);
  static bool _SetProperty(NPObject *npobj, NPIdentifier name, const NPVariant *value);
  static bool _RemoveProperty(NPObject *npobj, NPIdentifier name);
  static bool _Enumerate(NPObject *npobj, NPIdentifier **identifier, uint32_t *count);
  static bool _Construct(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result);

protected:
  NPP mNpp;
};

#define DECLARE_NPOBJECT_CLASS_WITH_BASE(_class, ctor)                        \
static NPClass s##_class##_NPClass = {                                        \
  NP_CLASS_STRUCT_VERSION_CTOR,                                               \
  ctor,                                                                       \
  ScriptablePluginObjectBase::_Deallocate,                                    \
  ScriptablePluginObjectBase::_Invalidate,                                    \
  ScriptablePluginObjectBase::_HasMethod,                                     \
  ScriptablePluginObjectBase::_Invoke,                                        \
  ScriptablePluginObjectBase::_InvokeDefault,                                 \
  ScriptablePluginObjectBase::_HasProperty,                                   \
  ScriptablePluginObjectBase::_GetProperty,                                   \
  ScriptablePluginObjectBase::_SetProperty,                                   \
  ScriptablePluginObjectBase::_RemoveProperty,                                \
  ScriptablePluginObjectBase::_Enumerate,                                     \
  ScriptablePluginObjectBase::_Construct                                      \
}

#define GET_NPOBJECT_CLASS(_class) &s##_class##_NPClass

void ScriptablePluginObjectBase::Invalidate() {
}

bool ScriptablePluginObjectBase::HasMethod(NPIdentifier name) {
  return false;
}

bool ScriptablePluginObjectBase::Invoke(NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result) {
  return false;
}

bool ScriptablePluginObjectBase::InvokeDefault(const NPVariant *args, uint32_t argCount, NPVariant *result){
  return false;
}

bool ScriptablePluginObjectBase::HasProperty(NPIdentifier name) {
  return false;
}

bool ScriptablePluginObjectBase::GetProperty(NPIdentifier name, NPVariant *result) {
  return false;
}

bool ScriptablePluginObjectBase::SetProperty(NPIdentifier name, const NPVariant *value) {
  return false;
}

bool ScriptablePluginObjectBase::RemoveProperty(NPIdentifier name) {
  return false;
}

bool ScriptablePluginObjectBase::Enumerate(NPIdentifier **identifier, uint32_t *count) {
  return false;
}

bool ScriptablePluginObjectBase::Construct(const NPVariant *args, uint32_t argCount, NPVariant *result) {
  return false;
}

// static
void ScriptablePluginObjectBase::_Deallocate(NPObject *npobj) {
  // Call the virtual destructor.
  delete (ScriptablePluginObjectBase *)npobj;
}

// static
void ScriptablePluginObjectBase::_Invalidate(NPObject *npobj) {
  ((ScriptablePluginObjectBase *)npobj)->Invalidate();
}

// static
bool ScriptablePluginObjectBase::_HasMethod(NPObject *npobj, NPIdentifier name) {
  return ((ScriptablePluginObjectBase *)npobj)->HasMethod(name);
}

// static
bool ScriptablePluginObjectBase::_Invoke(NPObject *npobj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result) {
  return ((ScriptablePluginObjectBase *)npobj)->Invoke(name, args, argCount, result);
}

// static
bool ScriptablePluginObjectBase::_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result) {
  return ((ScriptablePluginObjectBase *)npobj)->InvokeDefault(args, argCount, result);
}

// static
bool ScriptablePluginObjectBase::_HasProperty(NPObject * npobj, NPIdentifier name) {
  return ((ScriptablePluginObjectBase *)npobj)->HasProperty(name);
}

// static
bool ScriptablePluginObjectBase::_GetProperty(NPObject *npobj, NPIdentifier name, NPVariant *result) {
  return ((ScriptablePluginObjectBase *)npobj)->GetProperty(name, result);
}

// static
bool ScriptablePluginObjectBase::_SetProperty(NPObject *npobj, NPIdentifier name, const NPVariant *value) {
  return ((ScriptablePluginObjectBase *)npobj)->SetProperty(name, value);
}

// static
bool ScriptablePluginObjectBase::_RemoveProperty(NPObject *npobj, NPIdentifier name) {
  return ((ScriptablePluginObjectBase *)npobj)->RemoveProperty(name);
}

// static
bool ScriptablePluginObjectBase::_Enumerate(NPObject *npobj, NPIdentifier **identifier, uint32_t *count) {
  return ((ScriptablePluginObjectBase *)npobj)->Enumerate(identifier, count);
}

// static
bool ScriptablePluginObjectBase::_Construct(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result) {
  return ((ScriptablePluginObjectBase *)npobj)->Construct(args, argCount, result);
}


class ConstructablePluginObject : public ScriptablePluginObjectBase
{
public:
  ConstructablePluginObject(NPP npp)
    : ScriptablePluginObjectBase(npp)
  {
  }

  virtual bool Construct(const NPVariant *args, uint32_t argCount, NPVariant *result);
};

static NPObject * AllocateConstructablePluginObject(NPP npp, NPClass *aClass) {
  return new ConstructablePluginObject(npp);
}

DECLARE_NPOBJECT_CLASS_WITH_BASE(ConstructablePluginObject, AllocateConstructablePluginObject);

bool ConstructablePluginObject::Construct(const NPVariant *args, uint32_t argCount, NPVariant *result) {
  printf("Creating new ConstructablePluginObject!\n");

  NPObject *myobj = NPN_CreateObject(mNpp, GET_NPOBJECT_CLASS(ConstructablePluginObject));
  if (!myobj)
    return false;

  OBJECT_TO_NPVARIANT(myobj, *result);

  return true;
}

class ScriptablePluginObject : public ScriptablePluginObjectBase
{
public:
  ScriptablePluginObject(NPP npp)
    : ScriptablePluginObjectBase(npp)
  {
  }

  virtual bool HasMethod(NPIdentifier name);
  virtual bool HasProperty(NPIdentifier name);
  virtual bool GetProperty(NPIdentifier name, NPVariant *result);
  virtual bool Invoke(NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result);
  virtual bool InvokeDefault(const NPVariant *args, uint32_t argCount, NPVariant *result);
};

static NPObject * AllocateScriptablePluginObject(NPP npp, NPClass *aClass) {
  return new ScriptablePluginObject(npp);
}

DECLARE_NPOBJECT_CLASS_WITH_BASE(ScriptablePluginObject, AllocateScriptablePluginObject);

bool ScriptablePluginObject::HasMethod(NPIdentifier name) {
  return (name == tirIsCameraInitialized ||
		  name == tirIsCameraRunning ||
		  name == tirRecenter ||
		  name == tirGetMatrix);
}


	

bool ScriptablePluginObject::HasProperty(NPIdentifier name) {
  return (name == getMatrix0 || name == getMatrix1 || name == getMatrix2 || name == getMatrix3 ||
          name == getMatrix4 || name == getMatrix5 || name == getMatrix6 || name == getMatrix7 || 
          name == getMatrix8 || name == getMatrix9 || name == getMatrix10 || name == getMatrix11 ||
          name == getMatrix12 || name == getMatrix13 || name == getMatrix14 || name == getMatrix15
          );
}

bool ScriptablePluginObject::GetProperty(NPIdentifier name, NPVariant *result) {
	VOID_TO_NPVARIANT(*result);

	if(name == getMatrix0) {
		DOUBLE_TO_NPVARIANT((double)matrixData[0], *result); return true;
	} else if(name == getMatrix1) {
		DOUBLE_TO_NPVARIANT((double)matrixData[1], *result); return true;
	} else if(name == getMatrix2) {
		DOUBLE_TO_NPVARIANT((double)matrixData[2], *result); return true;
	} else if(name == getMatrix3) {
		DOUBLE_TO_NPVARIANT((double)matrixData[3], *result); return true;
	} else if(name == getMatrix4) {
		DOUBLE_TO_NPVARIANT((double)matrixData[4], *result); return true;
	} else if(name == getMatrix5) {
		DOUBLE_TO_NPVARIANT((double)matrixData[5], *result); return true;
	} else if(name == getMatrix6) {
		DOUBLE_TO_NPVARIANT((double)matrixData[6], *result); return true;
	} else if(name == getMatrix7) {
		DOUBLE_TO_NPVARIANT((double)matrixData[7], *result); return true;
	} else if(name == getMatrix8) {
		DOUBLE_TO_NPVARIANT((double)matrixData[8], *result); return true;
	} else if(name == getMatrix9) {
		DOUBLE_TO_NPVARIANT((double)matrixData[9], *result); return true;
	} else if(name == getMatrix10) {
		DOUBLE_TO_NPVARIANT((double)matrixData[10], *result); return true;
	} else if(name == getMatrix11) {
		DOUBLE_TO_NPVARIANT((double)matrixData[11], *result); return true;
	} else if(name == getMatrix12) {
		DOUBLE_TO_NPVARIANT((double)matrixData[12], *result); return true;
	} else if(name == getMatrix13) {
		DOUBLE_TO_NPVARIANT((double)matrixData[13], *result); return true;
	} else if(name == getMatrix14) {
		DOUBLE_TO_NPVARIANT((double)matrixData[14], *result); return true;
	} else if(name == getMatrix15) {
		DOUBLE_TO_NPVARIANT((double)matrixData[15], *result); return true;
	}

	return true;
}

bool ScriptablePluginObject::Invoke(NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result) {
	if(name == tirIsCameraInitialized) {
		INT32_TO_NPVARIANT(tirIsCameraInitialized_(), *result);
		return true;

	} else if(name == tirIsCameraRunning) {
		INT32_TO_NPVARIANT(tirIsCameraRunning_(), *result);
		return true;

	} else if(name == tirRecenter) {
		tirRecenter_(); 
		return true;

	} else if(name == tirGetMatrix) {
		tirGetMatrix_(matrixData);
		//matrixData = wGetMatrix();
		return true;
	}

	return false;
}

bool ScriptablePluginObject::InvokeDefault(const NPVariant *args, uint32_t argCount, NPVariant *result) {
  return true;
}

CPlugin::CPlugin(NPP pNPInstance) : m_pNPInstance(pNPInstance), m_pNPStream(NULL), m_bInitialized(false), m_pScriptableObject(NULL) {
#ifdef XP_WIN
	m_hWnd = NULL;
#endif
	tirIsCameraInitialized = NPN_GetStringIdentifier("tirIsCameraInitialized");
	tirIsCameraRunning = NPN_GetStringIdentifier("tirIsCameraRunning");
	tirRecenter = NPN_GetStringIdentifier("tirRecenter");

	tirGetMatrix = NPN_GetStringIdentifier("tirGetMatrix");
	getMatrix0 = NPN_GetStringIdentifier("getMatrix0");
	getMatrix1 = NPN_GetStringIdentifier("getMatrix1");
	getMatrix2 = NPN_GetStringIdentifier("getMatrix2");
	getMatrix3 = NPN_GetStringIdentifier("getMatrix3");
	getMatrix4 = NPN_GetStringIdentifier("getMatrix4");
	getMatrix5 = NPN_GetStringIdentifier("getMatrix5");
	getMatrix6 = NPN_GetStringIdentifier("getMatrix6");
	getMatrix7 = NPN_GetStringIdentifier("getMatrix7");
	getMatrix8 = NPN_GetStringIdentifier("getMatrix8");
	getMatrix9 = NPN_GetStringIdentifier("getMatrix9");
	getMatrix10 = NPN_GetStringIdentifier("getMatrix10");
	getMatrix11 = NPN_GetStringIdentifier("getMatrix11");
	getMatrix12 = NPN_GetStringIdentifier("getMatrix12");
	getMatrix13 = NPN_GetStringIdentifier("getMatrix13");
	getMatrix14 = NPN_GetStringIdentifier("getMatrix14");
	getMatrix15 = NPN_GetStringIdentifier("getMatrix15");

	if(tirDLL) {
		tirSetupCamera_ = (tirSetupCameraProto)GetProcAddress(tirDLL,"tirSetupCamera");
		tirShutdownCamera_ = (tirShutdownCameraProto)GetProcAddress(tirDLL,"tirShutdownCamera");
		tirIsCameraInitialized_ = (tirIsCameraInitializedProto)GetProcAddress(tirDLL,"tirIsCameraInitialized");
		tirStartCamera_ = (tirStartCameraProto)GetProcAddress(tirDLL,"tirStartCamera");
		tirStopCamera_ = (tirStopCameraProto)GetProcAddress(tirDLL,"tirStopCamera");
		tirIsCameraRunning_ = (tirIsCameraRunningProto)GetProcAddress(tirDLL,"tirIsCameraRunning");
		tirRecenter_ = (tirRecenterProto)GetProcAddress(tirDLL,"tirRecenter");
		tirGetMatrix_ = (tirGetMatrixProto)GetProcAddress(tirDLL,"tirGetMatrix");
	}
}

CPlugin::~CPlugin()
{
  if (sWindowObj)
    NPN_ReleaseObject(sWindowObj);
  if (m_pScriptableObject)
    NPN_ReleaseObject(m_pScriptableObject);

  sWindowObj = 0;
}

#ifdef XP_WIN
static LRESULT CALLBACK PluginWinProc(HWND, UINT, WPARAM, LPARAM);
static WNDPROC lpOldProc = NULL;
#endif

NPBool CPlugin::init(NPWindow* pNPWindow) {
  if(pNPWindow == NULL)
    return false;

#ifdef XP_WIN
  m_hWnd = (HWND)pNPWindow->window;
  if(m_hWnd == NULL)
    return false;

  // subclass window so we can intercept window messages and
  // do our drawing to it
  lpOldProc = SubclassWindow(m_hWnd, (WNDPROC)PluginWinProc);

  // associate window with our CPlugin object so we can access
  // it in the window procedure
  SetWindowLongPtr(m_hWnd, GWLP_USERDATA, (LONG_PTR)this);
#endif

  m_Window = pNPWindow;

  m_bInitialized = true;
  return true;
}

void CPlugin::shut() {
  tirStopCamera_();
  tirShutdownCamera_();

#ifdef XP_WIN
  // subclass it back
  SubclassWindow(m_hWnd, lpOldProc);
  m_hWnd = NULL;
#endif

  m_bInitialized = false;
}

NPBool CPlugin::isInitialized() {
  return m_bInitialized;
}

int16_t CPlugin::handleEvent(void* event) {
  return 0;
}

NPObject * CPlugin::GetScriptableObject() {
  if (!m_pScriptableObject) {
    m_pScriptableObject = NPN_CreateObject(m_pNPInstance, GET_NPOBJECT_CLASS(ScriptablePluginObject));
  }

  if (m_pScriptableObject) {
    NPN_RetainObject(m_pScriptableObject);
  }

  return m_pScriptableObject;
}

#ifdef XP_WIN
static LRESULT CALLBACK PluginWinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
  return DefWindowProc(hWnd, msg, wParam, lParam);
}
#endif
